package com.bvbot.core.bot;

import static com.bvbot.core.Logger.log;

import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;

import com.bvbot.core.BvBotDateFormatter;
import com.bvbot.core.BvBotException;

public class PricesHistory {

	private LinkedList<Prices> prices = new LinkedList<Prices>();

	private static final long PRICE_MAX_LIFETIME_IN_MILLIS = 86400000;

	private static final long MILLIS_IN_A_MINUTE = 60000;

	private Prices currentPrice;

	public PricesHistory() {
	}

	public Prices addCurrentPrice(Prices price) {
		addPrice(price);
		return price;
	}

	public void addPrice(Prices price) {
		if (price != null) {
			currentPrice = price;
			prices.add(currentPrice);
			removeOldPrices();
			Date oldestDate = getOldestPriceDate();
			if (oldestDate != null) {
				Date limitDate = new Date(System.currentTimeMillis()
						- PRICE_MAX_LIFETIME_IN_MILLIS - 1000);
				if (oldestDate.before(limitDate)) {
					log("Price leak : there is a price with a date "
							+ BvBotDateFormatter.get().format(oldestDate)
							+ " but limit date is "
							+ BvBotDateFormatter.get().format(limitDate));
				}
			}
		}
	}

	private Date getOldestPriceDate() {
		Date result = null;
		Iterator<Prices> it = prices.iterator();
		while (it.hasNext()) {
			Prices p = it.next();
			if (p != null) {
				Date d = p.getPriceDate();
				if (d != null) {
					if (result == null) {
						result = d;
					} else {
						if (d.before(result)) {
							result = d;
						}
					}
				}
			}
		}
		return result;
	}

	private void removeOldPrices() {
		Date limit = new Date(System.currentTimeMillis()
				- PRICE_MAX_LIFETIME_IN_MILLIS);
		prices = retainOnlyPriceAfter(limit, prices);
	}

	private LinkedList<Prices> retainOnlyPriceAfter(Date limit,
			LinkedList<Prices> list) {
		LinkedList<Prices> cleanedList = new LinkedList<Prices>();
		Iterator<Prices> it = list.iterator();
		while (it.hasNext()) {
			Prices price = it.next();
			Date date = price.getPriceDate();
			if (date != null) {
				if (date.after(limit)) {
					cleanedList.add(price);
				}
			}
		}
		return cleanedList;
	}

	public LinkedList<Prices> getPrices() {
		return prices;
	}

	public void setPrices(LinkedList<Prices> prices) {
		this.prices = prices;
	}

	public Prices getCurrentPrice() {
		return currentPrice;
	}

	public void setCurrentPrice(Prices currentPrice) {
		this.currentPrice = currentPrice;
	}

	private Prices getExtremePriceBeforeNowInMillis(long millis, boolean isHigh) {
		if (millis < 0) {
			BvBotException.exit("Incorret given parameter : " + millis);
		}
		Date limit = new Date(System.currentTimeMillis() - millis);
		LinkedList<Prices> workingList = retainOnlyPriceAfter(limit, prices);
		Prices result = computeExtremePrice(workingList, isHigh);
		return result;
	}

	public Prices getMaxPriceBeforeNowMinutes(int i) {
		if (i < 0) {
			BvBotException.exit("Incorret given parameter : " + i);
		}
		Prices result = getExtremePriceBeforeNowInMillis(
				MILLIS_IN_A_MINUTE * i, true);
		return result;
	}

	public Prices getMinPriceBeforeNowMinutes(int i) {
		if (i < 0) {
			BvBotException.exit("Incorret given parameter : " + i);
		}
		Prices result = getExtremePriceBeforeNowInMillis(
				MILLIS_IN_A_MINUTE * i, false);
		return result;
	}

	private Prices computeExtremePrice(LinkedList<Prices> workingList,
			boolean isHigh) {
		Prices result = null;
		Iterator<Prices> it = workingList.iterator();
		while (it.hasNext()) {
			Prices wp = it.next();
			if (result == null) {
				result = wp;
			} else {
				if (isHigh) {
					if (result.getGoldWorldValue() < wp.getGoldWorldValue()) {
						result = wp;
					}
				} else {
					if (result.getGoldWorldValue() > wp.getGoldWorldValue()) {
						result = wp;
					}
				}

			}
		}
		return result;
	}

	public void clearHistory() {
		prices = new LinkedList<Prices>();
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((currentPrice == null) ? 0 : currentPrice.hashCode());
		result = prime * result + ((prices == null) ? 0 : prices.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		PricesHistory other = (PricesHistory) obj;
		if (currentPrice == null) {
			if (other.currentPrice != null)
				return false;
		} else if (!currentPrice.equals(other.currentPrice))
			return false;
		if (prices == null) {
			if (other.prices != null)
				return false;
		} else if (!prices.equals(other.prices))
			return false;
		return true;
	}

}
